package com.luhuiguo.cms.server.service.impl;

import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.stereotype.Service;

import com.luhuiguo.cms.server.entity.Channel;
import com.luhuiguo.cms.server.service.ArticleService;
import com.luhuiguo.cms.server.service.ChannelService;
import com.luhuiguo.cms.server.util.PathUtils;
import com.luhuiguo.cms.server.util.SecurityUtils;

@Service
// @Transactional
public class ChannelServiceImpl implements ChannelService {

    @PersistenceUnit
    private EntityManagerFactory emf;

    @Autowired
    private ArticleService articleService;

    // @PersistenceContext
    // private EntityManager em;

    @Override
    public Channel getChannel(Long id) {
        EntityManager em = emf.createEntityManager();
        try {
            return em.find(Channel.class, id);
        } finally {
            em.close();
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Channel> findChannels() {
        EntityManager em = emf.createEntityManager();
        try {
            Query q = em.createQuery("SELECT FROM Channel ORDER BY path");
            List<Channel> list = q.getResultList();
            list.size();
            return list;
        } finally {
            em.close();
        }

    }

    @SuppressWarnings("unchecked")
    @Override
    public Channel getChannelByPath(String path) {
        EntityManager em = emf.createEntityManager();
        try {
            Query q = em.createQuery("SELECT FROM Channel WHERE path=:path");
            q.setParameter("path", path);
            List<Channel> list = q.getResultList();
            if (list.size() > 0) {
                return list.get(0);
            } else {
                return null;
            }
        } finally {
            em.close();
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Channel> findChannelsByParentId(Long parentId, int... rowStartIdxAndCount) {
        EntityManager em = emf.createEntityManager();
        try {
            Query q = em.createQuery("SELECT FROM Channel WHERE parentId = :parentId ORDER BY rank DESC, lastModified");
            q.setParameter("parentId", parentId);
            if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
                int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
                if (rowStartIdx > 0) {
                    q.setFirstResult(rowStartIdx);
                }
                if (rowStartIdxAndCount.length > 1) {
                    int rowCount = Math.max(0, rowStartIdxAndCount[1]);
                    if (rowCount > 0) {
                        q.setMaxResults(rowCount);
                    }
                }
            }
            List<Channel> list = q.getResultList();
            list.size();
            return list;
        } finally {
            em.close();
        }

    }

    @SuppressWarnings("unchecked")
    @Override
    public void saveChannel(Channel channel) {
        EntityManager em = emf.createEntityManager();
        try {
            channel.setPath(channel.getName());

            if (null != channel.getParentId()) {
                Channel p = em.find(Channel.class, channel.getParentId());
                if (p != null) {
                    channel.setPath(StringUtils.removeStart(p.getPath() + PathUtils.SLASH + channel.getName(),
                            PathUtils.SLASH));
                }
            }

            String path = channel.getPath();

            Query q = em.createQuery("SELECT FROM Channel WHERE path=:path");
            q.setParameter("path", path);
            List<Channel> list = q.getResultList();
            if (list.size() > 0) {
                throw new DataIntegrityViolationException("Path already exists: " + path);
            }

            channel.setCreated(new Date());
            channel.setCreatedBy(SecurityUtils.getCurrentUsername());
            channel.setLastModified(channel.getCreated());
            channel.setLastModifiedBy(channel.getCreatedBy());
            em.persist(channel);
        } finally {
            em.close();
        }

    }

    @Override
    public void deleteChannel(Long id) {
        EntityManager em = emf.createEntityManager();
        try {
            em.remove(em.find(Channel.class, id));
        } finally {
            em.close();
        }
    }

    @Override
    public Channel updateChannel(Channel channel) {
        EntityManager em = emf.createEntityManager();
        try {
            Channel c = em.find(Channel.class, channel.getId());
            if (null == c) {
                throw new DataRetrievalFailureException("Channel not found: " + channel.getId());
            }

            BeanUtils.copyProperties(channel, c, new String[]{ "id", "name", "path", "created", "createdBy" });
            c.setLastModified(new Date());
            c.setLastModifiedBy(SecurityUtils.getCurrentUsername());
            return em.merge(c);
        } finally {
            em.close();
        }
    }

    @Override
    public Channel updateChannel2(Channel channel) {
        EntityManager em = emf.createEntityManager();
        try {
            Channel c = em.find(Channel.class, channel.getId());
            if (null == c) {
                throw new DataRetrievalFailureException("Channel not found: " + channel.getId());
            }

            BeanUtils.copyProperties(channel, c, new String[]{ "id", "name", "created", "createdBy" });
            //c.setLastModified(new Date());
            //c.setLastModifiedBy(SecurityUtils.getCurrentUsername());
            return em.merge(c);
        } finally {
            em.close();
        }
    }

    @Override
    public void deleteChannelCascade(Long id) {

        List<Channel> list = findChannelsByParentId(id);

        for (Channel c : list) {
            deleteChannelCascade(c.getId());
        }
        articleService.deleteArticleByChannelId(id);
        deleteChannel(id);

    }

}
